package com.fw.render;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import com.fw.exception.RenderException;
import com.fw.utils.JsonUtils;

/**
 * JsonRender.
 * <p>
 * IE 不支持content type 为 application/json, 在 ajax 上传文件完成后返回 json时 IE 提示下载文件,<br>
 * 解决办法是使用： render(new JsonRender(params).forIE());
 */
public class JsonRender extends Render {
	
	private static final long serialVersionUID = 8668689384426859387L;
	/**
	 * http://zh.wikipedia.org/zh/MIME
	 * 在wiki中查到: 尚未被接受为正式数据类型的subtype，可以使用x-开始的独立名称（例如application/x-gzip）
	 * 所以以下可能要改成 application/x-json
	 * 
	 * 通过使用firefox测试,struts2-json-plugin返回的是 application/json, 所以暂不改为 application/x-json
	 * 1: 官方的 MIME type为application/json, 见 http://en.wikipedia.org/wiki/MIME_type
	 * 2: IE 不支持 application/json, 在 ajax 上传文件完成后返回 json时 IE 提示下载文件
	 */
	private static final String contentType = "application/json;charset=" + getEncoding();
	private static final String contentTypeForIE = "text/html;charset=" + getEncoding();
	private boolean forIE = false;
	private static int convertDepth = 8;

	public JsonRender forIE() {
		forIE = true;
		return this;
	}

	private String jsonText;
	private String[] attrs;

	public JsonRender() {

	}

	@SuppressWarnings("serial")
	public JsonRender(final String key, final Object value) {
		if (key == null) {
			throw new IllegalArgumentException("The parameter key can not be null.");
		}
		this.jsonText = JsonUtils.mapToJson(new HashMap<String, Object>() {
			{
				put(key, value);
			}
		}, convertDepth);
	}

	public JsonRender(String[] attrs) {
		if (attrs == null) {
			throw new IllegalArgumentException("The parameter attrs can not be null.");
		}
		this.attrs = attrs;
	}

	public JsonRender(String jsonText) {
		if (jsonText == null) {
			throw new IllegalArgumentException("The parameter jsonString can not be null.");
		}
		this.jsonText = jsonText;
	}
	
	public JsonRender(Object object) {
		if (object == null) {
			throw new IllegalArgumentException("The parameter object can not be null.");
		}
		this.jsonText = JsonUtils.toJson(object, convertDepth);
	}
	
	public static void setConvertDepth(int convertDepth) {
		if (convertDepth < 2) {
			throw new IllegalArgumentException("convert depth can not less than 2.");
		}
		JsonRender.convertDepth = convertDepth;
	}
	
	public void render() {
		if (jsonText == null) {
			buildJsonText();
		}
		
		PrintWriter writer = null;
		try {
			response.setHeader("Pragma", "no-cache");	// HTTP/1.0 caches might not implement Cache-Control and might only implement Pragma: no-cache
			response.setHeader("Cache-Control", "no-cache");
			response.setDateHeader("Expires", 0);
			
			response.setContentType(forIE ? contentTypeForIE : contentType);
			writer = response.getWriter();
	        writer.write(jsonText);
	        writer.flush();
		} catch (IOException e) {
			throw new RenderException(e);
		}
		finally {
			if (writer != null)
				writer.close();
		}
	}
	
	@SuppressWarnings({"rawtypes", "unchecked"})
	private void buildJsonText() {
		Map map = new HashMap();
		if (attrs != null) {
			for (String key : attrs) {
				map.put(key, request.getAttribute(key));
			}
		} else {
			Enumeration<String> attrs = request.getAttributeNames();
			while (attrs.hasMoreElements()) {
				String key = attrs.nextElement();
				Object value = request.getAttribute(key);
				map.put(key, value);
			}
		}

		this.jsonText = JsonUtils.mapToJson(map, convertDepth);
	}
}